/* replace.c, Ait Emacs, Kevin Bloom, BSD 3-Clause, 2023 */

#include <string.h>
#include "header.h"
#include "termbox.h"

/* These aren't used anywhere else, so I'm fine with leaving them out of
   the header.
*/
char pquery[STRBUF_M];
char pwith[STRBUF_M];

/*search for a string and replace it with another string */
void query_replace(void)
{
  point_t o_point = curbp->b_point;
  point_t l_point = -1;
  point_t found, mark;
  char question[STRBUF_L];
  int slen, rlen;   /* length of search and replace strings */
  int numsub = 0;   /* number of substitutions */
  int ask = TRUE;
  int c;
  struct tb_event ev;
  const char *qmsg = "Query replace";
  char querymsg[STRBUF_M*2 + strlen(qmsg) + 17];


  if(pquery[0] != '\0' && pwith[0] != '\0') {
    sprintf(querymsg, "%s (default %s -> %s): ", qmsg, pquery, pwith);
  } else {
    sprintf(querymsg, "%s: ", qmsg);
  }

  memset(searchtext, 0, STRBUF_M);
  memset(replace, 0, STRBUF_M);

  if (!getinput(querymsg, (char*)searchtext, STRBUF_M, F_CLEAR, TRUE))
    return;

  if(searchtext[0] == '\0') {
    strncpy(searchtext, pquery, STRBUF_M);
    strncpy(replace, pwith, STRBUF_M);
  } else if (!getinput("With: ", (char*)replace, STRBUF_M, F_CLEAR, TRUE))
    return;

  strncpy(pquery, searchtext, STRBUF_M);
  strncpy(pwith, replace, STRBUF_M);

  slen = strlen(searchtext);
  rlen = strlen(replace);

  /* build query replace question string */
  sprintf(question, "Replace '%s' with '%s' ? ", searchtext, replace);

  /* scan through the file, from point */
  numsub = 0;
  while(TRUE) {
    found = search_forward(curbp, curbp->b_point, searchtext);

    /* if not found set the point to the last point of replacement, or where we started */
    if (found == -1) {
      curbp->b_point = (l_point == -1 ? o_point : l_point);
      break;
    }

    curbp->b_point = found;
    /* search_forward places point at end of search, move to start of search */
    curbp->b_point -= slen;
    search_dir = 1;
    found_point = found;

    if (ask == TRUE) {
      msg(question);
      clrtoeol(question, MSGLINE);

    qprompt:
      update_display(TRUE);
      if(tb_poll_event(&ev) != TB_OK) return;
      if(!ev.mod)
        c = ev.ch;
      else
        c = ev.key;

      switch (c) {
      case 'y': /* yes, substitute */
        break;

      case 'n': /* no, find next */
        curbp->b_point = found; /* set to end of search string */
        continue;

      case '!': /* yes/stop asking, do the lot */
        ask = FALSE;
        found_point = -1;
        break;

      case TB_KEY_CTRL_G:
      case TB_KEY_ESC: /* esc */
        found_point = -1;
      case TB_KEY_ENTER:
      case 'q': /* controlled exit */
        found_point = -1;
        return;

      default: /* help me */
        msg("(y)es, (n)o, (!)do the rest, (q)uit");
        goto qprompt;
      }
    }

    mark = curbp->b_mark;
    curbp->b_mark = curbp->b_point + slen;
    undoset(REPLACE, rlen);
    curbp->b_mark = mark;

    if (rlen > slen) {
      movegap(curbp, found);
      /*check enough space in gap left */
      if (rlen - slen < curbp->b_egap - curbp->b_gap)
        growgap(curbp, rlen - slen);
      /* shrink gap right by r - s */
      curbp->b_gap = curbp->b_gap + (rlen - slen);
    } else if (slen > rlen) {
      movegap(curbp, found);
      /* stretch gap left by s - r, no need to worry about space */
      curbp->b_gap = curbp->b_gap - (slen - rlen);
    } else {
      /* if rlen = slen, we just overwrite the chars, no need to move gap */
    }

    /* now just overwrite the chars at point in the buffer */
    l_point = curbp->b_point;
    memcpy(ptr(curbp, curbp->b_point), replace, rlen * sizeof (char_t));
    curbp->b_flags |= B_MODIFIED;
    curbp->b_point = found - (slen - rlen); /* end of replcement */
    numsub++;
  }
  found_point = -1;
  msg("%d substitutions", numsub);
}
